from google.appengine.ext import db
from data import models

class Error(Exception):
	pass
class InvalidModelNameError(Error, models.MissingModelError):
	pass
class ColumnNamesError(Error):
	pass
class UnsupportedReferenceError(Error):
	pass
class ListWithUnspecifiedTargetError(Error):
	pass

cache={}
cache_keys={}

def get_ref_obj(target, str, use_cache=True):
	target_name=target.__name__
	if target_name not in cache:
		cache[target_name]={}
	if use_cache and str in cache[target_name]:
		return cache[target_name][str]

	if not target.get_human_key():
		raise UnsupportedReferenceError(target)
	cond=target.get_human_key()+' = '
	res=target.all().filter(cond, str).get()
	if res:
		cache[target_name][str]=res
	return res

def get_ref_obj_key(target,  str):
	target_name=target.__name__
	if target_name not in cache_keys:
		cache_keys[target_name]={}
	if str in cache_keys[target_name]:
		return cache_keys[target_name][str]
		
	if not target.get_human_key():
		raise UnsupportedReferenceError(target)
	cond=target.get_human_key()+' = '
	entity=target.all().filter(cond, str).get()
	if not entity:
		return None
	res=entity.key()
	cache_keys[target_name][str]=res
	return res

class ref_aux:
	def __init__(self, ref):
		self.ref=ref
	def fn(self, x):
		return get_ref_obj(self.ref, x)
	
def importData(data_string):
	try:
		lines=data_string.split("\n")
		model_name=lines[0]
		model=models.get(model_name)
		properties=model.properties()

		if lines[1]=='':
			raise ColumnNamesError()
		cols=lines[1].split('\t')
		processors={}
		for col in cols:
			prop_obj=properties[col]
			if isinstance(prop_obj, db.ReferenceProperty):
				ref=prop_obj.reference_class
				processors[col]=ref_aux(ref).fn
			elif isinstance(prop_obj, db.BooleanProperty):
				processors[col]=lambda x: x.lower()=='true'
			elif isinstance(prop_obj, db.IntegerProperty):
				processors[col]=lambda x: int(x)
			elif isinstance(prop_obj, db.FloatProperty):
				processors[col]=lambda x: float(x)
			elif isinstance(prop_obj, db.ListProperty):
				if not hasattr(prop_obj, '_target'):
					raise ListWithUnspecifiedTargetError(col)
				target=prop_obj._target
				processors[col]=lambda x: [get_ref_obj_key(target,  sl) for sl in x.split(',')]
			else:
				processors[col]=lambda x: x
				
		if model.get_human_key() and model.get_human_key() in cols:
			model_key=model.get_human_key()
		else:
			model_key=None
		
		stats={'added': 0, 'refreshed':0}
		
		data_strings=lines[2:]
		for data_string in data_strings:
			if data_string=='':
				continue
			data=data_string.split('\t')
			params={}
			for i in range(0,len(data)):
				col=cols[i]
				params[col]=processors[col](data[i])
			
			obj=None
			if model_key:
				obj=get_ref_obj(model,  params[model_key], use_cache=False)
			if obj==None:
				obj=model(**params)
				stats['added']+=1
			else:
				del(params[model_key])
				for col in params:
					setattr(obj,  col,  params[col])
				stats['refreshed']+=1
			obj.put()
	except models.MissingModelError, name:
		raise InvalidModelNameError(name)
	return stats
